//  Copyright (c) 2010, Mark Sivill, Sales Engineering, Salesforce.com Inc.
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
//  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
//  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
//  Neither the name of the salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
//  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
//  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
//  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// History
//
// Version  Date            Author          Comments
// 1.0      21-10-2010      Mark Sivill     Initial version
// 1.1      22-10-2010      Keith Nixon     Update to Mark's initial version
// 1.2      24-10-2010      Keith Nixon     Continuing with updates.
// 1.3      25-10-2010      Mark Sivill     Continuing with updates.
// 1.4      26-10-2010      Mark Sivill     Updated to add sequence to visit activity
// 1.5      18-01-2011      Keith Nixon     Further updates as per spreadsheet from A.Coveney
//
// For a given visit create a one page 'wizard' that allow multiple Activitys to be selected
//
// NOTE code not bulkified
//
public class OutletVisitActivityExtension {

    private String visitId;
    private Outlet_Visit__c visitRec;
    private Integer selectedPlannedActivityId;
    private id idOfObjectiveActivityToRemove;
    private id idOfObjectiveActivityToAdd;
    private String accountId;    
          
    private List<Outlet_Visit_Activity__c> visitActivities;     // current 'working set' taken from screen at start up
    private Set<String> objectiveNames; // A set of all the unique objective names used for sorting the activities, knixon
    private List<OutletVisitObjectives> objectives; // This is the list returned to the visualforce page.
    private List<OutletVisitActivityWrapper> databaseActivities;   // current list of activities saved in salesforce
    private List<OutletVisitActivityWrapper> defaultAccountActivities;   // default activities determined from account
    private List<OutletVisitActivityWrapper> adhocActivities;   // current list of adhoc activities
      
    public OutletVisitActivityExtension(ApexPages.StandardSetController controller)  
    {
        
        //get the id of the outlet visit record.
        visitId = ApexPages.currentPage().getParameters().get('Id');
        
        // get the visit records based of the ids of the set controller.
        databaseActivities = getActivityWrappersByVisitId(visitId);

//System.Debug('********** OutletVisitActivityExtension.OutletVisitActivityExtension databaseActivities ' + databaseActivities);
        
        // determine adhoc activities from current activities
        adhocActivities = getAdhocActivities(databaseActivities);

//System.Debug('********** OutletVisitActivityExtension.OutletVisitActivityExtension adhocActivities ' + adhocActivities );

        //query for our outlet visit record.
        visitRec = getVisitByID(visitId);

//System.Debug('********** OutletVisitActivityExtension.OutletVisitActivityExtension visit ' + visitRec );
        
        //set the id of the account associated with the visit record.
        accountId = visitRec.Account__c;

        // determine default Account Activities and whether currently an outlet activity
        defaultAccountActivities = getDefaultActivitiesForAccount(accountId,databaseActivities);

//System.Debug('********** OutletVisitActivityExtension.OutletVisitActivityExtension defaultAccountActivities ' + defaultAccountActivities );

        // work out duration from values on screen
        calculateDurationInMinutes();
    }

    //
    // get default list of activities for a given account id (code not bulkified)
    // 
    public List<OutletVisitActivityWrapper> getDefaultActivitiesForAccount(String theId, List<OutletVisitActivityWrapper> databaseActivities)
    {
    
        List <OutletVisitActivityWrapper> defaultResult = new List <OutletVisitActivityWrapper>();
        Map<Id,Id> objectivesMap = new Map<Id,Id>(); // a map of the objective ids to the associated objective ids. Assumes the same
                                                     // objective is not added more than once to the outlet.
        
        //get all the ids from outlet_objective__c that look up to objective__c first.
        List <Outlet_Objectives__c> objectives = [SELECT Id, o.Objective__c, o.Account__c FROM Outlet_Objectives__c o WHERE o.Account__c = :theId];
        
        Set <ID> objectiveIDs = new Set <ID>();
        for (Outlet_Objectives__c objs: objectives)
        {
            objectiveIDs.add(objs.Objective__c);
            objectivesMap.put(objs.Objective__c,objs.Id);
        }
        
        // 
        // get all the outlet visit activities associated with the existing outlet visits
        // for this outlet. The following set will contain the id of the objective activities
        // associated with all of the existing outlet visit activities.
        // Added by knixon, 18-01-2011
        //
        Set<Id> existingOutletVisitActivities = new Set<Id>();
        for(Outlet_Visit_Activity__c ova : [Select Id, Objective_Activity__c , Objective_Activity__r.Name
                                            From Outlet_Visit_Activity__c 
                                            Where Outlet_Visits_LU__r.Account__c =: theId AND Status__c = 'completed']) {
            existingOutletVisitActivities.add(ova.Objective_Activity__c);
        }
        
        //
        // create a map from objective activity to visit activity so can quickly
        // test if objective activity is matched up. Map of objective activity to outlet visit activity
        //
        Map<Id,Outlet_Visit_Activity__c> objectiveVisitActivityMap = new Map<Id,Outlet_Visit_Activity__c>();
        for (OutletVisitActivityWrapper wrapper:databaseActivities)
        {
            if(wrapper.visit.Objective_Activity__c != null)
            {
              objectiveVisitActivityMap.put(wrapper.visit.Objective_Activity__c, wrapper.visit);
            }
        }
        
        //now query down from objective to objective activity to retrieve the 
        //objective activities for each objective.
        for(Objective_Activity__c oa : [SELECT a.Id, a.Name, a.Full_Stock_Audit__c, a.Objective__c, a.Objective__r.Name, a.Duration_in_min__c, Priority_Val__c, Sequence__c 
                                        FROM Objective_Activity__c a 
                                        WHERE a.Objective__c IN :objectiveIDs
                                        ORDER BY a.Objective__r.Name])
        {
              // 
              // We only add the record to the list default result if it is not an existing outlet visit
              // activity that has already been completed. See 'existingOutletVisitActivities' set above.
              // Added by knixon, 18-01-2011
              //
              if (objectiveVisitActivityMap.containsKey(oa.id) && !existingOutletVisitActivities.contains(oa.Id))
              {
                 // ensure objective name is copied across 
                 objectiveVisitActivityMap.get(oa.id).Objective_Name__c = oa.Objective__r.Name;
                 
                 // copy across the outlet_objective__c lookup field
                 objectiveVisitActivityMap.get(oa.id).Outlet_Objective__c = objectivesMap.get(oa.Objective__c);

                 // selected (and database record already created for outlet visit activity
                 defaultResult.add(new OutletVisitActivityWrapper(objectiveVisitActivityMap.get(oa.id),true,true,oa.Full_Stock_Audit__c));
              }
              else
              {
                 // not selected
                 // check to see if the objective_activity__c is in ANY of the existing
                 // outlet visit activity records. If so then we don't add a blank record.
                 // Otherwise we do.
                 //
                 // Also we want to make sure it has not already been completed. See 
                 // 'existingOutletVisitActivities' set above.
                 // Added by knixon, 18-01-2011
                 //
                 if(!existingOutletVisitActivities.contains(oa.Id) && !existingOutletVisitActivities.contains(oa.Id))
                 {
                     Id outletObjectiveId; // added by knixon. Id of the outlet objective to link this visit
                                           // activity to.
                     Outlet_Visit_Activity__c visitActivity = new Outlet_Visit_Activity__c(); 
                     
                     // get the outlet objective record id for this outlet visit activity record. knixon                      
                     for(Outlet_Objectives__c oo : objectives)
                     {
                         if(oo.Account__c == theId && oo.Objective__c == oa.Objective__c)
                         {
                             visitActivity.Outlet_Objective__c = oo.Id;
                             break;
                         }
                     }
                
                     visitActivity.Duration_in_Min__c = oa.Duration_in_min__c;
                     visitActivity.Name = oa.Name;
                     visitActivity.Objective_Activity__c = oa.Id;
                     visitActivity.Objective_Name__c = oa.Objective__r.Name;
                     visitActivity.Outlet_Visits_LU__c = visitId;
                     visitActivity.Status__c = 'Planned';
                     visitActivity.Sequence__c = oa.Sequence__c;
                     visitActivity.Objective_Priority_Val__c = oa.Priority_Val__c;
                     
                     //
                     //set the outlet_objective__c lookup to the
                     //outlet objective that has the same objective
                     //id as that of the current objective_activity__c
                     //objective__c reference field. added by knixon
                     //                    
                     visitActivity.Outlet_Objective__c = objectivesMap.get(oa.Objective__c); 
                         
                     defaultResult.add(new OutletVisitActivityWrapper(visitActivity,false,false,oa.Full_Stock_Audit__c));
                 }
                 else
                 {
                     System.debug('\n\n***** OBJECTIVE ACTIVITY ' + oa.Name + ' WAS COMPLETED IN A PREVIOUS VISIT');
                 }
              }
        } 
        
        return defaultResult;
    }

    //
    // determine adhoc activities
    //
    public List<OutletVisitActivityWrapper> getAdhocActivities( List<OutletVisitActivityWrapper> allActivities)
    {
        List<OutletVisitActivityWrapper> adhocResult = new List<OutletVisitActivityWrapper>();
        for (OutletVisitActivityWrapper wrapper : allActivities)
        {
           if ( wrapper.visit.Objective_Activity__c == null )
           {
             adhocResult.add(wrapper);
           }
        }

        orderRecordNumbers(adhocResult); 
        
        return adhocResult;
    }
    
    //
    // used to order activities which is used by delete button in GUI
    //
    public void orderRecordNumbers( List<OutletVisitActivityWrapper> theActivities)
    {
        Integer counter=0;
        for (OutletVisitActivityWrapper wrapper : theActivities)
        {
           wrapper.recordCounter = counter;
           counter++;
        }
    }
    
    //
    // work out duration in minutes    
    //
    public void calculateDurationInMinutes()
    {
        visitRec.Planned_Duration__c = 0;
        for (OutletVisitActivityWrapper wrapper:defaultAccountActivities)
        {
           if (( wrapper.isSelected == true ) && (wrapper.visit.Duration_in_Min__c != null))
           {
             visitRec.Planned_Duration__c = visitRec.Planned_Duration__c + wrapper.visit.Duration_in_Min__c; 
           }
        }

        for (OutletVisitActivityWrapper wrapper:adhocActivities)
        {
           if ( wrapper.visit.Duration_in_Min__c != null )
           {
             visitRec.Planned_Duration__c = visitRec.Planned_Duration__c + wrapper.visit.Duration_in_Min__c; 
           }
        }
    }
    
    //
    // get outlet visit activity details
    //
    public List<OutletVisitActivityWrapper> getActivityWrappersByVisitId(String theId)
    {
       
       List<OutletVisitActivityWrapper> result = new List<OutletVisitActivityWrapper>();
       
       for(Outlet_Visit_Activity__c ova : [SELECT va.Id, va.Outlet_Objective__c, va.Objective_Activity__c, va.Objective_Priority_Val__c, va.Objective_Activity__r.Full_Stock_Audit__c, va.Name, va.Objective_Activity__r.Objective__c, va.Objective_Name__c, va.Status__c, va.Duration_in_Min__c
                                                FROM Outlet_Visit_Activity__c va
                                                WHERE va.Outlet_Visits_LU__c = :theId])
       {
            result.add(new OutletVisitActivityWrapper(ova, true, false, ova.Objective_Activity__r.Full_Stock_Audit__c));
       } 
       
       return result;
    } 

    public List<OutletVisitActivityWrapper> getAdhocActivities()
    {
        return adhocActivities;
    }

    public List<OutletVisitActivityWrapper> getDefaultAccountActivities()
    {
        return defaultAccountActivities;
    }
    
    public List<OutletVisitObjectives> getObjectives()
    {
        // we will sort all of the objective activities (defaulAccountActivities) by
        // the associated objective name.
        List<OutletVisitObjectives> ovo = new List<OutletVisitObjectives>();
        objectiveNames = new Set<String>();
        
        for(OutletVisitActivityWrapper ovaw : defaultAccountActivities)
        {
            // add the objective name from the activity.
            objectiveNames.add(ovaw.visit.Objective_Name__c);
        }
        
        // now loop through the activity wrapper list again and create
        // a list for each unique objective name that contains a list of
        // activities.
        for(String objectiveName : objectiveNames)
        {
            List<OutletVisitActivityWrapper> activities = new List<OutletVisitActivityWrapper>();
            
            for(OutletVisitActivityWrapper ovaw : defaultAccountActivities)
            {
                if(ovaw.visit.Objective_Name__c.equalsIgnoreCase(objectiveName))
                {
                    activities.add(ovaw);
                }
            }
            
            if(!activities.isEmpty())
            {
                OutletVisitObjectives obj = new OutletVisitObjectives(objectiveName, activities);
                ovo.add(obj);
            }
        }
        
        return ovo;
    }

    //
    // get visit detail for a given visit id
    // 
    public Outlet_Visit__c getVisitByID(String theId)
    {
        return [SELECT v.id, v.name, v.Account__c, v.Account__r.Name, v.Planned_time__c, v.Planned_Duration__c FROM Outlet_Visit__c v WHERE v.Id = :theId]; 
    }
 
    public Outlet_Visit__c getVisit()
    {
        return visitRec;
    }
       
    //The following getter and setter are included to overcome
    //a visualforce bug of a commandbutton not being able to post
    //an <apex:param> value back to the controller.
    Public Integer getPM()
    {
        return selectedPlannedActivityId;
    }
    
    public void setPM(Integer value)
    {
        this.selectedPlannedActivityId = value;
    }    
    
    public String getObjectiveActivityToAdd()
    {
        return idOfObjectiveActivityToAdd;
    }
    
    public void setObjectiveActivityToAdd(String value)
    {
        this.idOfObjectiveActivityToAdd = value;
    }
    
    public String getObjectiveActivityToRemove()
    {
        return idOfObjectiveActivityToRemove;
    } 
    
    public void setObjectiveActivityToRemove(String value)
    {
        this.idOfObjectiveActivityToRemove = value;
    }       
    
    public PageReference refresh()
    {
        calculateDurationInMinutes();
        return null;
    }

    public PageReference foo()
    {
        return null;
    }

    //
    // allow all objectives to be selected
    //
    public PageReference selectAllObjectives()
    {

        for(OutletVisitActivityWrapper wrapper:defaultAccountActivities)
        {
          wrapper.isSelected = true;
        }
        calculateDurationInMinutes();
        return null;
    }
     
    //
    // get currently defined outlet visit acitivity, "planned activities"
    //
    public List<Outlet_Visit_Activity__c> getActivitiesByVisitID(String theId)
    {    
        return [SELECT va.Id, va.Objective_Activity__c, va.Name, va.Duration_in_Min__c FROM Outlet_Visit_Activity__c va WHERE Outlet_Visits_LU__c = :theId];
    }
   
    //This method will add a new blank 'Outlet_Visit_Activity__c' record to
    //the list of Outlet_Visit_Activity__c records that were queried when the 
    //page first loaded. This function is called by the "New Adhoc Activity" 
    //button on the visualforce page
    public PageReference newAdhocActivity()
    {
    
       Outlet_Visit_Activity__c visitActivity = new Outlet_Visit_Activity__c();
           
       visitActivity.Outlet_Visits_LU__c = visitId;
       visitActivity.Duration_in_Min__c = 0;
       visitActivity.Status__c = 'Planned';

       adhocActivities.add(new OutletVisitActivityWrapper(visitActivity,false,true,false));

       orderRecordNumbers(adhocActivities);
       calculateDurationInMinutes();

       return null;
    }
    
    //This method will save all of the planned activities.
    //If there are any planned activities where the name is 
    //the same as the id, it will be deleted.
    public PageReference saveActivities()
    {
    
    //System.Debug('********** OutletVisitActivityExtension.saveActivities databaseActivities size ' + databaseActivities.size() + ' ' + databaseActivities);
    //System.Debug('********** OutletVisitActivityExtension.saveActivities defaultAccountActivities size ' + defaultAccountActivities.size() + ' ' + defaultAccountActivities);
    //System.Debug('********** OutletVisitActivityExtension.saveActivities adhocActivities size ' + adhocActivities.size() + ' ' + adhocActivities);
    
        // list of changes to make to database
        List<Outlet_Visit_Activity__c> deleteActivities = new List<Outlet_Visit_Activity__c>();
        List<Outlet_Visit_Activity__c> createActivities = new List<Outlet_Visit_Activity__c>();
        List<Outlet_Visit_Activity__c> updateActivities = new List<Outlet_Visit_Activity__c>();
    
        // work out changes for default account activity
        for(OutletVisitActivityWrapper wrapper:defaultAccountActivities)
        {
          // change has happend so update to database required
          if (wrapper.isOnDatabase != wrapper.isSelected)
          {
            // if here then it has been deleted
            if (wrapper.isOnDatabase)
            {
              deleteActivities .add(wrapper.visit);
            }
            
            // if here then it has been added
            if (!wrapper.isOnDatabase)
            {
              createActivities .add(wrapper.visit);
            }
          }
        }
    
        //
        // work out orginal list of adhoc activities
        //
        List<OutletVisitActivityWrapper> orginalAdhocActivities = getAdhocActivities(databaseActivities);
        Set<Outlet_Visit_Activity__c> deletedFromScreenActivities = new set<Outlet_Visit_Activity__c>();
                
        // list that will eventually contain deleted activities        
        for(OutletVisitActivityWrapper wrapper:orginalAdhocActivities)
        {
          deletedFromScreenActivities.add(wrapper.visit);
        }
        
        // work out changes for adhoc activity
        for(OutletVisitActivityWrapper wrapper:adhocActivities)
        {
           // ignore blank records
           if (!((wrapper.visit.Name == null) || (wrapper.visit.Name.equals(''))))
           {          
             // new record
             if(wrapper.visit.id == null)
             {
               createActivities.add(wrapper.visit);
             }
             else
             {
               updateActivities.add(wrapper.visit);
               
               // remove update from 'orginal' list so end up with activities to delete
               deletedFromScreenActivities.remove(wrapper.visit);
             }             
           }
        }
        
        // add deleted list to other deleted records
        List<Outlet_Visit_Activity__c> temp = new List<Outlet_Visit_Activity__c>();
        temp.addAll(deletedFromScreenActivities);
        deleteActivities.addAll(temp);
    
    //System.Debug('********** OutletVisitActivityExtension.saveActivities deleteActivities size ' + deleteActivities.size() + ' ' + deleteActivities);
    //System.Debug('********** OutletVisitActivityExtension.saveActivities createActivities size ' + createActivities.size() + ' ' + createActivities);
    //System.Debug('********** OutletVisitActivityExtension.saveActivities updateActivities size ' + updateActivities.size() + ' ' + updateActivities);
    
        // make changes to database
        if (deleteActivities.size()>0)
        {
          delete deleteActivities;
        }
    
        if (createActivities.size()>0)
        {
          insert createActivities;
          
          // check the product ranges of the associated objectives to see
          // if they are null. If they are, we create visit stock audit items
          // for all the associated products of the outlet stock.
          
          
          // now we insert any visit stock audit items
          // that may need to be inserted.
          List<Visit_Stock_Audit_Item__c> stockAuditItems = new List<Visit_Stock_Audit_Item__c>();
          for(Outlet_Stock__c os : [Select Id, Product__r.Product_Range__c From Outlet_Stock__c Where Account__c =: accountId])
          {
            Visit_Stock_Audit_Item__c vsai = new Visit_Stock_Audit_Item__c();
            vsai.Outlet_Stock__c = os.Id;
            vsai.Product_Range__c = os.Product__r.Product_Range__c;
            stockAuditItems.add(vsai);
          }
          
          List<Visit_Stock_Audit_Item__c> stockAuditItemsToInsert = new List<Visit_Stock_Audit_Item__c>();
          if(!stockAuditItems.isEmpty())
          {
             for(Outlet_Visit_Activity__c ova : createActivities)
             {
                for(Integer i=0; i<stockAuditItems.size(); i++)
                {
                  // Clone each entry in the stockAuditItems list
                  Visit_Stock_Audit_Item__c v = stockAuditItems.get(i).clone(false,true);
                  v.Outlet_Visit_Activity__c = ova.Id;
                  stockAuditItemsToInsert.add(v);
                }
             }
             
             insert stockAuditItemsToInsert;
          }
        }
        
        if (updateActivities.size()>0)
        {
          update updateActivities;
        }
       
       // update duration in minutes to visit record               
       upsert visitRec;
                      
       PageReference newPage = new ApexPages.StandardController(visitRec).view();
       newPage.setRedirect(true);
       return newPage;
    }

    //
    // allow users to cancel their changes
    //
    public PageReference cancel()
    {
       PageReference newPage = new ApexPages.StandardController(visitRec).view();
       newPage.setRedirect(true);
       return newPage;
    }
        
    // This method will delete a selected planned activity by clicking
    // the 'delete' button on the visualforce page beside a planeed activity
    // record
    public PageReference deleteSelectedActivity()
    {   
//System.debug('***** ID OF ACTIVITY TO DELETE: ' + selectedPlannedActivityId);

        adhocActivities.remove(selectedPlannedActivityId);
        
        orderRecordNumbers(adhocActivities);
        calculateDurationInMinutes();
        
        return null;
    }
    
    // record that this default account activity has been de-selected
    public PageReference removeObjectiveActivity()
    {
//System.debug('\n***** REMOVE OBJECTIVE ACTIVITY: ' + idOfObjectiveActivityToRemove);
//System.Debug('********** OutletVisitActivityExtension.removeObjectiveActivity defaultAccountActivities size ' + defaultAccountActivities.size() + ' ' + defaultAccountActivities);

        for(OutletVisitActivityWrapper wrapper:defaultAccountActivities)
        {
          if (wrapper.visit.Objective_Activity__c == idOfObjectiveActivityToRemove)
          {
            wrapper.isSelected = false;
            break;
          }
        }

//System.Debug('********** OutletVisitActivityExtension.removeObjectiveActivity defaultAccountActivities size ' + defaultAccountActivities.size() + ' ' + defaultAccountActivities);
        calculateDurationInMinutes();

        return null;
    }
    
    // record that this default account activity has been selected
    public PageReference addObjectiveActivity()
    {
//System.debug('\n***** ADD OBJECTIVE ACTIVITY: ' + idOfObjectiveActivityToAdd);
//System.Debug('********** OutletVisitActivityExtension.addObjectiveActivity defaultAccountActivities size ' + defaultAccountActivities.size() + ' ' + defaultAccountActivities);

        for(OutletVisitActivityWrapper wrapper:defaultAccountActivities)
        {

//System.Debug('********** OutletVisitActivityExtension.addObjectiveActivity id match? - ' + wrapper.visit.Objective_Activity__c + ' ' + idOfObjectiveActivityToAdd);

          if (wrapper.visit.Objective_Activity__c == idOfObjectiveActivityToAdd)
          {
            wrapper.isSelected = true;
            break;
          }
        }            

//System.Debug('********** OutletVisitActivityExtension.addObjectiveActivity defaultAccountActivities size ' + defaultAccountActivities.size() + ' ' + defaultAccountActivities);
        calculateDurationInMinutes();

        return null;
    }
    
    class OutletVisitActivityWrapper
    {
        public Outlet_Visit_Activity__c visit { get; set; }
        public Boolean isOnDatabase { get; set; }
        public Boolean isSelected { get; set; } // on screen
        public Boolean fullStockAudit { get; set; }  //indicates if full stock audit needed. Added by knixon, 19-01-2011.
        public Integer recordCounter { get; set; } // used for adhoc record deleting (as record may not have an Id yet)

        public OutletVisitActivityWrapper(Outlet_Visit_Activity__c visit, Boolean isOnDatabase, Boolean isSelected, Boolean fullStockAudit)
        {
            this.visit = visit;
            this.isOnDatabase = isOnDatabase;
            this.isSelected = isSelected;
            this.fullStockAudit = fullStockAudit;
        }
    }

    //
    // This class is a wrapper for the objectives, each of which contains the outlet visit
    // activities for that objective.
    // Added by knixon, 19-01-2011
    //
    class OutletVisitObjectives 
    {
        public String objectiveName { get; set; }
        public List<OutletVisitActivityWrapper> activities { get; set; }
        
        public OutletVisitObjectives(String objectiveName, List<OutletVisitActivityWrapper> activities) 
        {
            this.objectiveName = objectiveName;
            this.activities = activities;
        }
    }
}